/*
 * PhotonRTOS础光实时操作系统 -- gic-v2中断控制器文件
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <photon/errno.h>
#include <photon/irq.h>
#include <photon/init.h>
#include <photon/printk.h>
#include <photon/smp_lock.h>
#include <photon/smp.h>
#include <asm/types.h>
#include <asm/io.h>
#include <asm/isr1.h>

#if defined(CONFIG_ARM_GIC_V2)

#define NR_GIC_CPU_IF 8
static uint8_t gic_cpu_map[NR_GIC_CPU_IF];
extern bool cat_2_interrupt;

union gic_base {
	uintptr_t common_base;
	void  *  *percpu_base;
};

struct gic_chip_data {
	union gic_base dist_base;
	union gic_base cpu_base;
	uint32_t gic_irqs;
};

static struct gic_chip_data g_gic_data;
static struct smp_lock irq_controller_lock =
			SMP_LOCK_UNLOCKED(irq_controller_lock);
#define gic_data_dist_base(d)	((d)->dist_base.common_base)
#define gic_data_cpu_base(d)	((d)->cpu_base.common_base)
#define gic_set_base_accessor(d, f)



static __maybe_unused void
gic_raise_softirq(const struct cpumask *mask, uint32_t irq)
{
	int32_t cpu;
	uintptr_t flags, map = 0;

	smp_lock_irqsave(&irq_controller_lock, flags);

	/**
	 * 逻辑CPU编号转换为物理编号
	 */
	for_each_cpu(cpu, mask)
		map |= gic_cpu_map[cpu];

	/*
	 * 屏障，确保其他核的可见性
	 */
	dmb(ishst);

	writel_relaxed(map << 16 | irq,
		gic_data_dist_base(&g_gic_data) + GIC_DIST_SOFTINT);

	smp_unlock_irqrestore(&irq_controller_lock, flags);
}

static uint8_t gic_get_cpumask(const struct gic_chip_data *gic)
{
	uintptr_t base = gic_data_dist_base(gic);
	uint32_t mask, i;

	mask = 0;
	for (i = 0U; i < 32U; i += 4U) {
		mask = readl_relaxed(base + (uintptr_t)(GIC_DIST_TARGET + i));
		mask |= mask >> 16;
		mask |= mask >> 8;
		if (mask != 0U) {
			break;
		}
	}

	return (uint8_t)mask;
}

static void gic_dist_config(uintptr_t base, uint32_t gic_irqs,
				void (*sync_access)(void))
{
	uint32_t i;

	/*
	 * Set all global interrupts to be level triggered, active low.
	 */
	for (i = 32U; i < gic_irqs; i += 16U) {
		writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
					base + (uintptr_t)(GIC_DIST_CONFIG + (i / 4U)));
	}

	/*
	 * Set priority on all global interrupts.
	 */
	for (i = 32U; i < gic_irqs; i += 4U) {
		writel_relaxed(GICD_INT_DEF_PRI_X4, base + (uintptr_t)(GIC_DIST_PRI + i));
	}

	/*
	 * Disable all interrupts.  Leave the PPI and SGIs alone
	 * as they are enabled by redistributor registers.
	 */
	for (i = 32U; i < gic_irqs; i += 32U) {
		writel_relaxed(GICD_INT_EN_CLR_X32,
					base + (uintptr_t)(GIC_DIST_ENABLE_CLEAR + (i / 8U)));
	}

	if (sync_access != NULL) {
		sync_access();
	}
}

static void gic_dist_init(const struct gic_chip_data *gic)
{
	uint32_t i;
	uint32_t cpumask;
	uint32_t gic_irqs = gic->gic_irqs;
	uintptr_t base = gic_data_dist_base(gic);

	writel_relaxed(GICD_DISABLE, base + (uintptr_t)GIC_DIST_CTRL);

	/*
	 * Set all global interrupts to this CPU only.
	 */
	cpumask = gic_get_cpumask(gic);
	cpumask |= cpumask << 8;
	cpumask |= cpumask << 16;
	for (i = 32U; i < gic_irqs; i += 4U) {
		writel_relaxed(cpumask, base + (uintptr_t)(GIC_DIST_TARGET + ((i * 4U) / 4U)));
	}

	/* 硬件中断号为71的TTC1中断，指向CPU1（糟糕的做法，需要重构） */
	writel_relaxed(0x02010101, base + GIC_DIST_TARGET + 0x44);

	gic_dist_config(base, gic_irqs, NULL);

	writel_relaxed(GICD_ENABLE, base + (uintptr_t)GIC_DIST_CTRL);
}

static void gic_cpu_if_up(void)
{
	uintptr_t cpu_base = gic_data_cpu_base(&g_gic_data);
	uint32_t bypass = 0;

	/*
	* Preserve bypass disable bits to be written back later
	*/
	bypass = readl(cpu_base + (uintptr_t)GIC_CPU_CTRL);
	bypass &= GICC_DIS_BYPASS_MASK;

	writel_relaxed(bypass | GICC_ENABLE, cpu_base + (uintptr_t)GIC_CPU_CTRL);
}

static void gic_cpu_config(uintptr_t base, void (*sync_access)(void))
{
	int32_t i;

	/*
	 * Deal with the banked PPI and SGI interrupts - disable all
	 * PPI interrupts, ensure all SGI interrupts are enabled.
	 */
	writel_relaxed(GICD_INT_EN_CLR_PPI, base + (uintptr_t)GIC_DIST_ENABLE_CLEAR);
	writel_relaxed(GICD_INT_EN_SET_SGI, base + (uintptr_t)GIC_DIST_ENABLE_SET);

	/*
	 * Set priority on PPI and SGI interrupts
	 */
	for (i = 0; i < 32; i += 4) {
		writel_relaxed(GICD_INT_DEF_PRI_X4,
			base + (uintptr_t)(GIC_DIST_PRI + ((i * 4) / 4)));
	}
	/*复位当前运行优先级*/
	writel_relaxed(0xFF, base + GIC_CPU_RPR);
	if (sync_access != NULL) {
		sync_access();
	}
}

static void gic_set_cpu_bpr(uint32_t val)
{
	uintptr_t cpu_base = gic_data_cpu_base(&g_gic_data);
	/**
	 * 设置抢占级别
	 * 0：高七位决定抢占级别：ggggggg.s
	 * 1：高六位决定抢占级别：gggggg.ss
	 * 2：高五位决定抢占级别：ggggg.sss
	 * 3：高四位决定抢占级别：gggg.ssss
	 * 4：高三位决定抢占级别：ggg.sssss
	 * 5：高二位决定抢占级别：gg.ssssss
	 * 6：高一位决定抢占级别：g.sssssss
	 * 7：无抢占		 .ssssssss
	 */
	writel_relaxed(val, cpu_base + GIC_CPU_BPR);
}

static void gic_cpu_init(const struct gic_chip_data *gic)
{
	uintptr_t dist_base = gic_data_dist_base(gic);
	uintptr_t base = gic_data_cpu_base(gic);
	uint32_t cpu_mask;
	int32_t i;
	int cpu = smp_processor_id();

	/*
	 * Get what the GIC says our CPU mask is.
	 */
	cpu_mask = gic_get_cpumask(gic);
	gic_cpu_map[cpu] = cpu_mask;

	/*
	 * Clear our mask from the other map entries in case they're
	 * still undefined.
	 */
	for (i = 0; i < NR_GIC_CPU_IF; i++) {
		if (i != cpu) {
			gic_cpu_map[i] &= ~((uint8_t)cpu_mask);
		}
	}

	gic_cpu_config(dist_base, NULL);

	writel_relaxed(GICC_INT_PRI_THRESHOLD, base + (uintptr_t)GIC_CPU_PRIMASK);
	gic_set_cpu_bpr(3);

	gic_cpu_if_up();
}

static void gic_pm_init(struct gic_chip_data *gic)
{
}

static inline uintptr_t gic_dist_base(void)
{
	return gic_data_dist_base(&g_gic_data);
}

static inline uintptr_t gic_cpu_base(void)
{
	return gic_data_cpu_base(&g_gic_data);
}

/*
 * Routines to acknowledge, disable and enable interrupts
 */
static void gic_poke_irq(uint32_t hw_irq, uint32_t offset)
{
	uint32_t mask = (uint32_t)1 << (hw_irq % 32U);
	writel_relaxed(mask, gic_dist_base() + (uintptr_t)(offset + ((hw_irq / 32U) * 4U)));
}

static uint32_t gic_read_irq(uint32_t hw_irq, uint32_t offset)
{
	uint32_t bit = hw_irq % 32;
	uint32_t ret = readl_relaxed(gic_dist_base() + offset + (hw_irq / 32) * 4);
	return ret >> bit & 1;
}

static void gic_mask_irq(uint32_t hw_irq)
{
	gic_poke_irq(hw_irq, GIC_DIST_ENABLE_CLEAR);
}

static void gic_unmask_irq(uint32_t hw_irq)
{
	gic_poke_irq(hw_irq, GIC_DIST_ENABLE_SET);
}

static void gic_eoi_irq(uint32_t hw_irq)
{
	writel_relaxed(hw_irq, gic_cpu_base() + (uintptr_t)GIC_CPU_EOI);
}

static void gic_clear_pending(uint32_t hw_irq)
{
	gic_poke_irq(hw_irq, GIC_DIST_PENDING_CLEAR);
}

static uint32_t gic_get_irq_status(uint32_t hw_irq)
{
	uint32_t ret = gic_read_irq(hw_irq, GIC_DIST_ENABLE_SET);
	return ret;
}

static int32_t gic_configure_irq(uint32_t irq, uint32_t type,
			uintptr_t base, void (*sync_access)(void))
{
	uint32_t enablemask = (uint32_t)1 << (irq % 32U);
	uint32_t enableoff = (irq / 32U) * 4U;
	uint32_t confmask = (uint32_t)0x2 << ((irq % 16U) * 2U);
	uint32_t confoff = (irq / 16U) * 4U;
	bool enabled = false;
	uint32_t val, oldval;
	int32_t ret = 0;

	/*
	 * Read current configuration register, and insert the config
	 * for "irq", depending on "type".
	 */
	oldval = readl_relaxed(base + (uintptr_t)(GIC_DIST_CONFIG + confoff));
	val = oldval;
	if ((type & HWIRQ_TYPE_LEVEL_MASK) != 0U) {
		val &= ~confmask;
	} else if ((type & HWIRQ_TYPE_EDGE_MASK) != 0U) {
		val |= confmask;
	} else {
		/* 其他情况不处理 */
	}

	/*
	 * As recommended by the spec, disable the interrupt before changing
	 * the configuration
	 */
	if ((readl_relaxed(base + (uintptr_t)(GIC_DIST_ENABLE_SET + enableoff))
		& enablemask) != 0U) {
		writel_relaxed(enablemask,
		base + (uintptr_t)(GIC_DIST_ENABLE_CLEAR + enableoff));
		if (sync_access != NULL) {
			sync_access();
		}
		enabled = true;
	}

	/*
	 * Write back the new configuration, and possibly re-enable
	 * the interrupt. If we tried to write a new configuration and failed,
	 * return an error.
	 */
	writel_relaxed(val, base + (uintptr_t)(GIC_DIST_CONFIG + confoff));
	if ((readl_relaxed(base + (uintptr_t)(GIC_DIST_CONFIG + confoff))
		!= val) && (val != oldval)) {
		ret = -EINVAL;
	}

	if (enabled) {
		writel_relaxed(enablemask,
		base + (uintptr_t)(GIC_DIST_ENABLE_SET + enableoff));
	}

	if (sync_access != NULL) {
		sync_access();
	}

	return ret;
}

int32_t gic_set_type(uint32_t hw_irq, uint32_t type)
{
	uintptr_t base = gic_dist_base();
	/* Interrupt configuration for SGIs can't be changed */
	if (hw_irq < 16U) {
		return -EINVAL;
	}

	/* SPIs have restrictions on the supported types */
	if ((hw_irq >= 32U) && (type != HWIRQ_TYPE_LEVEL_HIGH) &&
				(type != HWIRQ_TYPE_EDGE_RISING)) {
		return -EINVAL;
	}

	return gic_configure_irq(hw_irq, type, base, NULL);
}

/*GIC中最多只支持256个优先级*/
static int32_t gic_set_priority(uint32_t hw_irq, uint32_t pri)
{
	uintptr_t base = gic_dist_base();
	uint32_t pri_val;
	/*优先级寄存器的偏移*/
	uint32_t pri_n_off = (GIC_DIST_PRI + (hw_irq / 4) * 4);
	/*字节偏移*/
	uint32_t pri_byte_off = (hw_irq % 4);
	/*设置的优先级是否高于当前的优先级上限*/
	if (pri >= GICC_INT_PRI_THRESHOLD) {
		return -EINVAL;
	}
	/**
	 * 这里的实现比较丑陋，由于优先级只有高位可用，
	 * 当前启用了16级中断，故先左移4位
	 * 其实应该根据优先级门限设定左移位数
	 */
	pri = pri << 4;

	/*SPI才需要设置*/
	if (hw_irq < 32) {
		return -EINVAL;
	}
	pri_val = readl_relaxed(base + pri_n_off);
	/*对应的位先清零*/
	pri_val &= ~(0xFF << (pri_byte_off * 8));
	pri_val |= (pri << (pri_byte_off * 8));
	writel_relaxed(pri_val, base + pri_n_off);
	return 0;
}

static uint32_t gic_get_priority(uint32_t hw_irq)
{
	uintptr_t base = gic_dist_base();
	uint32_t pri_val;
	uint32_t pri_n_off = (GIC_DIST_PRI + (hw_irq / 4) * 4);
	/*字节偏移*/
	uint32_t pri_byte_off = (hw_irq % 4);

	pri_val = readl_relaxed(base + pri_n_off);
	/*先左移(3 - pri_byte_off) * 8个字节*/
	pri_val = pri_val << ((3 - pri_byte_off) * 8);
	/*再右移pri_byte_off * 8个字节，得到对应的中断优先级*/
	pri_val = pri_val >> 24;
	/*再16级中断优先级中，高四位有效*/
	pri_val = pri_val >> 4;
	return pri_val;
}

/**
 * 处理GIC中断的入口
 */
static void __exception_irq_entry gic_handle_irq(void *regs)
{
	uint32_t irqstat, irqnr;
	uintptr_t cpu_base = gic_data_cpu_base( &g_gic_data);

	do {
		irqstat = readl_relaxed(cpu_base + (uintptr_t)GIC_CPU_INTACK);
		irqnr = irqstat & GICC_IAR_INT_ID_MASK;
		/*如果是一类中断，那么直接在isr1中找对应的handler*/
		if (isr1[irqnr]) {
			isr1[irqnr]();
			gic_eoi_irq(irqnr);
			continue;
		}
		if ((irqnr > 15U) && (irqnr < 1021U) && cat_2_interrupt) {
			do_hard_irq(irqnr, regs);
			continue;
		}
		/*核间中断，R5不支持，可以去掉*/
		if (irqnr < 16U && cat_2_interrupt) {
			writel_relaxed(irqstat, cpu_base + (uintptr_t)GIC_CPU_EOI);
			do_IPI(irqnr, regs);
			continue;
		}
		break;
	} while (true);
}

void gic_init_bases(int32_t irq_start,
			   uintptr_t dist_base, uintptr_t cpu_base)
{
	/* uint32_t hwirq_base; */
	struct gic_chip_data *gic = &g_gic_data;
	uint32_t gic_irqs;
	int32_t i;
	/* int32_t irq_base; */

	gic->dist_base.common_base = dist_base;
	gic->cpu_base.common_base = cpu_base;
	gic_set_base_accessor(gic, gic_get_common_base);

	/*
	 * Initialize the CPU interface map to all CPUs.
	 * It will be refined as each CPU probes its ID.
	 */
	for (i = 0; i < NR_GIC_CPU_IF; i++) {
		gic_cpu_map[i] = 0xff;
	}

	/*
	 * Find out how many interrupts are supported.
	 * The GIC only supports up to 1020 interrupt sources.
	 */
	gic_irqs = readl_relaxed(gic_data_dist_base(gic) + (uintptr_t)GIC_DIST_CTR) & 0x1fU;

	gic_irqs = (gic_irqs + 1U) * 32U;
	if (gic_irqs > 1020U) {
		gic_irqs = 1020U;
	}
	gic->gic_irqs = gic_irqs;

 	/*更新全局数据*/
	smp_set_raise_ipi_call(gic_raise_softirq);
	set_chip_irq_handle(gic_handle_irq);

	gic_dist_init(gic);
	gic_cpu_init(gic);
	gic_pm_init(gic);
}

struct irq_controller os_irq_controller = {
	.set_trigger_type = gic_set_type,
	.mask = gic_mask_irq,
	.unmask = gic_unmask_irq,
	.ack = gic_eoi_irq,
	.clear_pending = gic_clear_pending,
	.get_status = gic_get_irq_status,
	.set_priority = gic_set_priority,
	.get_priority = gic_get_priority,
};

void irq_controller_init(void)
{
	uintptr_t cpu_base;
	uintptr_t dist_base;

	dist_base = (uintptr_t)0xf9000000U;
	cpu_base = (uintptr_t)0xf9001000U;

	gic_init_bases( -1, dist_base, cpu_base);

	/**
	* 中断钩子函数注册
	*/
	irq_count = g_gic_data.gic_irqs;
}

void gic_secondary_init(void)
{
	gic_cpu_init(&g_gic_data);
}

#endif